﻿using System;
using System.ComponentModel;
using System.IO;
using System.Threading;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Threading;
using C4F.VistaP2P.Common;

namespace C4F.VistaP2P.WPF.File
{
    /// <summary>
    /// Interaction logic for File.xaml
    /// </summary>
    public partial class FileControl : UserControl
    {
        /// <summary>
        /// just initializes and displays the control
        /// </summary>
        public FileControl()
        {
            InitializeComponent();           
        }

        /// <summary>
        /// Hooks up the default buttons if no custom controls were registered for at design time.
        /// </summary>
        /// <param name="sender">unused</param>
        /// <param name="e">unused</param>
        private void _FileControl_Loaded(object sender, RoutedEventArgs e)
        {
             if (SendFileButton == null)
            {
                SendFileButton = btnSendFile;
            }           

            if (StatusLog == null)
            {
                StatusLog = tbStatusLog;
            }
        }


        #region Dependency Properties
        /// <summary>
        /// The dependency property for the Sending File Button control. The dependency property is used so we can have databinding using the smart tag adorner
        /// </summary>
        public static DependencyProperty SendFileButtonProperty = DependencyProperty.Register(
           "SendFileButton", typeof(Button), typeof(FileControl), new PropertyMetadata(null, new PropertyChangedCallback(_SendFilePropertyChanged)));


        /// <summary>
        /// set and get the button used as the send button for the file control.
        /// </summary>
        public Button SendFileButton
        {
            get
            {
                try
                {
                    return (Button)this.Dispatcher.Invoke(
                       System.Windows.Threading.DispatcherPriority.Normal,
                       (DispatcherOperationCallback)delegate { return GetValue(SendFileButtonProperty); },
                       SendFileButtonProperty);
                }
                catch
                {

                    return (Button)SendFileButtonProperty.DefaultMetadata.DefaultValue;
                }
            }
            set
            {
                Dispatcher.Invoke(DispatcherPriority.Normal,
                  (SendOrPostCallback)delegate { SetValue(SendFileButtonProperty, value); },
                  value);
            }
        }


        /// <summary>
        /// If the send button was changed update the function that listens for the send button event ( _SendFileButton_Click)
        /// </summary>
        /// <param name="d">the file control</param>
        /// <param name="e">the old and new buttons</param>
        private static void _SendFilePropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            FileControl fc = (FileControl)d;
            if (e.OldValue != null)
            {
                //unregister from the old one...
                Button ob = (Button)e.NewValue;
                ob.Click -= new RoutedEventHandler(fc._SendFileButton_Click);
            }
            //register for the new events.
            Button nb = (Button)e.NewValue;
            nb.Click += new RoutedEventHandler(fc._SendFileButton_Click);
        }

        /// <summary>
        /// The dependency property for the StatusLog control. The dependency property is used so we can have databinding using the smart tag adorner
        /// </summary>
        public static DependencyProperty StatusLogProperty = DependencyProperty.Register(
           "StatusLog", typeof(TextBox), typeof(FileControl));

        /// <summary>
        /// set and get the textbox used as the status label file control.
        /// </summary>
        public TextBox StatusLog
        {
            get
            {
                try
                {
                    return (TextBox)this.Dispatcher.Invoke(
                       System.Windows.Threading.DispatcherPriority.Normal,
                       (DispatcherOperationCallback)delegate { return GetValue(StatusLogProperty); },
                       StatusLogProperty);
                }
                catch
                {

                    return (TextBox)StatusLogProperty.DefaultMetadata.DefaultValue;
                }
            }
            set
            {
                Dispatcher.Invoke(DispatcherPriority.Normal,
                  (SendOrPostCallback)delegate { SetValue(StatusLogProperty, value); },
                  value);
            }
        }

      

       

        #endregion

        #region public methods and events to use when completely overriding this control
        /// <summary>
        /// send a file to the other nodes in the network.
        /// </summary>
        /// <example>
        ///  mFile.SendFile("C:\Users\Rob\myTextFile.txt");
        /// </example>
        /// <param name="fullPathAndFileName">the fullpath and filename</param>
        public void SendFile(string fullPathAndFileName)
        {
            //send the file in another thread so the UI thread remains responsive...  
            if (fullPathAndFileName != null && System.IO.File.Exists(fullPathAndFileName))
            {
                BackgroundWorker worker;
                worker = new System.ComponentModel.BackgroundWorker();
                worker.DoWork += new System.ComponentModel.DoWorkEventHandler(_SendFileBGWorker);
                worker.RunWorkerAsync(fullPathAndFileName);
            }
            else
            {
                throw new ApplicationException(string.Format("File not found: {0}", fullPathAndFileName));
            }
        }

        /// <summary>
        /// This event is raised when a File is received from the mesh.
        /// </summary>
        public event EventHandler<FileChangedEventArgs> FileReceived;

        /// <summary>
        /// This event is raised when the status of the network has changed.
        /// </summary>
        public event EventHandler<StatusChangedEventArgs> StatusChanged;


        #endregion
                
        
        /// <summary>
        /// Handle the button click and open a dialog to send a file.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void _SendFileButton_Click(object sender, EventArgs e)
        {
            Microsoft.Win32.OpenFileDialog ofd;
            ofd = new Microsoft.Win32.OpenFileDialog();
            ofd.FileName = "openFileDialog1";
            ofd.Filter = "All Files (*.*)|*.*";
            ofd.InitialDirectory = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments);
            ofd.RestoreDirectory = true;
            ofd.Title = "Select a File";

            bool ok = (bool)ofd.ShowDialog();
            if (ok)
            {
                SendFile(ofd.FileName);
            }            
        }

        /// <summary>
        /// The NetworkManager control subscribes to this event so it can send a file to the mesh.
        /// </summary>
        internal event EventHandler<FileChangedEventArgs> SendingFile;

        /// <summary>
        /// _UpdateStatusLogHandler delegate used to ensure that the control is only changed by the thread that created it.
        /// search online for crossthreading issues for more information.
        /// </summary>
        /// <param name="message">the message to put into the log</param>
        private delegate void _UpdateStatusLogHandler(string message);
        void UpdateStatusLog(string message)
        {
            if (message != null)
            {
                if (StatusLog.CheckAccess())
                {
                    StatusLog.Text = message;
                }
                else
                {
                    StatusLog.Dispatcher.Invoke(System.Windows.Threading.DispatcherPriority.Normal, new _UpdateStatusLogHandler(UpdateStatusLog), message);                    
                    
                }
            }
        }

        /// <summary>
        /// The background worker that sends out the file.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void _SendFileBGWorker(object sender, DoWorkEventArgs e)
        {
           UpdateStatusLog(String.Format("Sending a File: {0}\r\n", (String)e.Argument));

            System.IO.Stream stream = System.IO.File.OpenRead((String)e.Argument);
            if (stream != null)
            {
                Packet packet = new Packet();
                packet.stream = stream;
                packet.fileName = System.IO.Path.GetFileName((String)e.Argument);
                MetadataFileInfo md = new MetadataFileInfo();
                md.ReadMetaData((String)e.Argument);
                //packet.fileName = (String)e.Argument;

                if (SendingFile != null)
                {
                    SendingFile(this, new FileChangedEventArgs(packet));
                }
            }
            UpdateStatusLog("File Sent\r\n");          
        }




        /// <summary>
        /// _FileReceivedHandler delegate used to ensure that the control is only changed by the thread that created it.
        /// search online for crossthreading issues for more information.
        /// </summary>
        /// <param name="FileChangedEventArgs">the message to put into the log</param>
        private delegate void _FileReceivedHandler(FileChangedEventArgs e);
        public void SetFileReceived(FileChangedEventArgs e)
        {

            //push us back to the UI thread so if we have to raise an event FileReceived it's on the controls thread...
            if (StatusLog.CheckAccess())
            {
                if (FileReceived != null)
                {
                    //let someone else handle it because they registered for the event..
                    FileReceived(this, e);
                }
                else
                {
                    //handle it ourselves
                    Stream stream = null;
                    try
                    {
                        System.Windows.Forms.DialogResult dr = System.Windows.Forms.MessageBox.Show(e.Packet.senderNodeName + " just sent you a file. Select OK to accept it, otherwise select Cancel", "A file was sent to you!", System.Windows.Forms.MessageBoxButtons.OKCancel);
                        if (dr == System.Windows.Forms.DialogResult.OK)
                        {
                            Microsoft.Win32.SaveFileDialog ofs;
                            ofs = new Microsoft.Win32.SaveFileDialog();
                            ofs.FileName = e.Packet.fileName;
                            ofs.Filter = "All Files (*.*)|*.*";
                            ofs.InitialDirectory = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments);
                            ofs.RestoreDirectory = true;                        
                            ofs.Title = e.Packet.senderNodeName + "Sent you a file.";

                            bool ok = (bool)ofs.ShowDialog();
                            if (ok)
                            {
                                stream = new FileStream(ofs.FileName, FileMode.Create, FileAccess.Write);
                                StreamingHelper.CopyStream(e.Packet.stream, stream);
                            }
                        }
                    }
                    catch (Exception)
                    {

                    }
                    finally
                    {
                        if (stream != null)
                            stream.Close();
                    }
                }
            }
            else
            {
                StatusLog.Dispatcher.Invoke(DispatcherPriority.Normal, new _FileReceivedHandler(SetFileReceived), e);
            }
        }

        /// <summary>
        /// _StatusChangedHandler delegate used to ensure that the control is only changed by the thread that created it.
        /// search online for crossthreading issues for more information.
        /// </summary>
        /// <param name="FileChangedEventArgs">the message to put into the log</param>
        private delegate void _StatusChangedHandler(string userName, StatusChangedEventArgs e);
        internal void SetStatusChanged(string userName, StatusChangedEventArgs e)
        {
            
            if (SendFileButton.CheckAccess())
            {
                if (userName == e.Member && e.NewNodeJoined)
                {
                    SendFileButton.IsEnabled = true;
                }

                if (userName == e.Member && e.NodeLeft)
                {
                    SendFileButton.IsEnabled = false;
                }

                //let anyone else know if they registered for the event.
                if (null != StatusChanged)
                {
                    StatusChanged(userName, e);
                }
            }
            else
            {
                SendFileButton.Dispatcher.Invoke(DispatcherPriority.Normal, new _StatusChangedHandler(SetStatusChanged),  userName, e);                
            }
        }
    }
}
